﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="TimeRecordsBrowserViewModel.cs" company="Mar3ek">
//   http://mar3ek.wordpress.com
// </copyright>
// <summary>
//   Defines the TimeRecordsBrowserViewModel type.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace Mar3ek.ColorHatTimeTracker.ViewModels
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Globalization;
    using System.IO;
    using System.Linq;
    using System.Runtime.CompilerServices;
    using System.Text;
    using System.Text.RegularExpressions;
    using System.Windows.Input;

    using ColorhatNet;

    using Mar3ek.ColorHatTimeTracker.Windows;

    using Microsoft.Win32;

    /// <summary>
    /// The time records browser view model.
    /// </summary>
    public class TimeRecordsBrowserViewModel : INotifyPropertyChanged
    {
        /// <summary>
        /// Stores the search results.
        /// </summary>
        private IEnumerable<TimeEntry> results;

        /// <summary>
        /// Stores the ColorHat API reference.
        /// </summary>
        private ColorhatApi apiInstance;

        /// <summary>
        /// Starting date filter.
        /// </summary>
        private DateTime startingDate;

        /// <summary>
        /// End date filter.
        /// </summary>
        private DateTime endDate;

        /// <summary>
        /// Project filter.
        /// </summary>
        private uint? projectId;

        /// <summary>
        /// Service filter.
        /// </summary>
        private uint? serviceId;

        /// <summary>
        /// Customer filter.
        /// </summary>
        private uint? customerId;

        /// <summary>
        /// Stores the customers.
        /// </summary>
        private IDictionary<uint, string> customers;

        /// <summary>
        /// Stores the projects.
        /// </summary>
        private IDictionary<uint, string> projects;

        /// <summary>
        /// Stores the services.
        /// </summary>
        private IDictionary<uint, string> services;

        /// <summary>
        /// Stores the total time.
        /// </summary>
        private string totalTime;

        /// <summary>
        /// Initializes a new instance of the <see cref="TimeRecordsBrowserViewModel"/> class.
        /// </summary>
        public TimeRecordsBrowserViewModel()
        {
            SearchCommand = new DelegateCommand(SearchAction);
            BreakdownsCommand = new DelegateCommand(BreakdowndsAction);
            ExportCommand = new DelegateCommand(ExportAction);

            startingDate = DateTime.Now.AddDays(-DateTime.Now.Day + 1);
            endDate = DateTime.Now;
        }

        /// <summary>
        /// The property changed event delegate.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Gets the search command.
        /// </summary>
        public ICommand SearchCommand { get; private set; }

        /// <summary>
        /// Gets or sets the breakdowns command.
        /// </summary>
        public ICommand BreakdownsCommand { get; set; }

        /// <summary>
        /// Gets or sets the results.
        /// </summary>
        public IEnumerable<TimeEntry> Results
        {
            get
            {
                return this.results;
            }

            set
            {
                this.results = value;
                this.OnPropertyChanged();
            }
        }

        /// <summary>
        /// Gets the customers.
        /// </summary>
        public IDictionary<uint, string> Customers
        {
            get
            {
                return this.customers;
            }

            private set
            {
                this.customers = value;
                this.OnPropertyChanged();
            }
        }

        /// <summary>
        /// Gets the projects.
        /// </summary>
        public IDictionary<uint, string> Projects
        {
            get
            {
                return this.projects;
            }

            private set
            {
                this.projects = value;
                this.OnPropertyChanged();
            }
        }

        /// <summary>
        /// Gets the services.
        /// </summary>
        public IDictionary<uint, string> Services
        {
            get
            {
                return this.services;
            }

            private set
            {
                this.services = value;
                this.OnPropertyChanged();
            }
        }

        /// <summary>
        /// Gets or sets the starting date.
        /// </summary>
        public DateTime StartingDate
        {
            get
            {
                return this.startingDate;
            }

            set
            {
                this.startingDate = value;
                this.OnPropertyChanged();
            }
        }

        /// <summary>
        /// Gets or sets the end date.
        /// </summary>
        public DateTime EndDate
        {
            get
            {
                return this.endDate;
            }

            set
            {
                this.endDate = value;
                this.OnPropertyChanged();
            }
        }

        /// <summary>
        /// Gets or sets the project id.
        /// </summary>
        public uint? ProjectId
        {
            get
            {
                return this.projectId;
            }

            set
            {
                this.projectId = value > 0 ? value : null;
                this.OnPropertyChanged();
            }
        }

        /// <summary>
        /// Gets or sets the service id.
        /// </summary>
        public uint? ServiceId
        {
            get
            {
                return this.serviceId;
            }

            set
            {
                this.serviceId = value > 0 ? value : null;
                this.OnPropertyChanged();
            }
        }

        /// <summary>
        /// Gets or sets the customer id.
        /// </summary>
        public uint? CustomerId
        {
            get
            {
                return this.customerId;
            }

            set
            {
                this.customerId = value > 0 ? value : null;
                this.OnPropertyChanged();
            }
        }

        /// <summary>
        /// Gets the total time.
        /// </summary>
        public string TotalTime
        {
            get
            {
                return totalTime;
            }

            private set
            {
                totalTime = value;
                this.OnPropertyChanged();
            }
        }

        /// <summary>
        /// Gets the export command.
        /// </summary>
        public ICommand ExportCommand { get; private set; }

        /// <summary>
        /// Initializes this view model.
        /// </summary>
        /// <param name="api">
        /// The API instance to use.
        /// </param>
        internal void Initialize(ColorhatApi api)
        {
            this.apiInstance = api;

            this.Customers = CreateDictionaryWithEmptyRecord(apiInstance.Customers);
            this.Projects = CreateDictionaryWithEmptyRecord(apiInstance.Projects);
            this.Services = CreateDictionaryWithEmptyRecord(apiInstance.Services);
        }

        /// <summary>
        /// The on property changed event invocator.
        /// </summary>
        /// <param name="propertyName">
        /// The property name.
        /// </param>
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            var handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        /// <summary>
        /// Creates a new dictionary containing the original data plus an empty record.
        /// </summary>
        /// <param name="readOnlyDictionary">
        /// The read only dictionary to use as a data source.
        /// </param>
        /// <returns>
        /// A dictionary containing the specified data, plus an empty record.
        /// </returns>
        private static Dictionary<uint, string> CreateDictionaryWithEmptyRecord(IEnumerable<KeyValuePair<uint, string>> readOnlyDictionary)
        {
            var result = new Dictionary<uint, string> { { 0, string.Empty } };

            foreach (var pair in readOnlyDictionary)
            {
                result.Add(pair.Key, pair.Value);
            }

            return result;
        }

        /// <summary>
        /// The search implementation.
        /// </summary>
        /// <param name="parameter">
        /// The command parameter.
        /// </param>
        private async void SearchAction(object parameter)
        {
            if (this.apiInstance == null)
            {
                return;
            }

            using (new WaitCursor())
            {
                Results = await this.apiInstance.GetTimeEntries(this.startingDate, this.endDate, this.CustomerId, this.ProjectId, this.ServiceId);
                var totalSpan = TimeSpan.FromTicks(Results.Sum(r => r.Duration.Ticks));
                var hours = (int)totalSpan.TotalMinutes / 60;
                var minutes = totalSpan.TotalMinutes % 60;

                TotalTime = string.Format("{0}:{1}", hours, minutes.ToString(CultureInfo.InvariantCulture).PadLeft(2, '0'));
            }
        }

        /// <summary>
        /// Opens the Breakdowns window.
        /// </summary>
        /// <param name="parameter">
        /// The command parameter.
        /// </param>
        private async void BreakdowndsAction(object parameter)
        {
            var w = new BreakdownsWindow();

            using (new WaitCursor())
            {
                var vm = (BreakdownsViewModel)w.DataContext;

                var entries = await this.apiInstance.GetTimeEntries(this.startingDate, this.endDate, this.CustomerId, this.ProjectId, this.ServiceId);

                vm.Initialize(entries, string.Format("{0} - {1}", this.startingDate.ToString("MMMM dd, yyyy"), this.endDate.ToString("MMMM dd, yyyy"))); 
            }

            w.ShowDialog();
        }

        /// <summary>
        /// Export the current results to a CSV file
        /// </summary>
        /// <param name="parameter">
        /// The command parameter.
        /// </param>
        private void ExportAction(object parameter)
        {
            var sfd = new SaveFileDialog
                          {
                              FileName = "Colorhat Export",
                              Filter = "Comma-separated values|*.csv",
                              Title = "Export Colorhat data..."
                          };

            var response = sfd.ShowDialog();

            if (response == false)
            {
                return;
            }

            var buffer = new StringBuilder();
            const string Header = "Date,Customer,Project,Service,User,Annotation,Time,Duration";

            buffer.AppendLine(Header);
            var invariant = CultureInfo.InvariantCulture;

            foreach (var segment in results)
            {
                buffer.AppendLine(
                    string.Format(
                        "{0},{1},{2},{3},{4},\"{5}\",{6},{7}",
                        segment.Date.ToString("d", invariant),
                        segment.Customer,
                        segment.Project,
                        segment.Service,
                        segment.User,
                        Regex.Replace(segment.Annotation, "(<br\\s+/>|\\n|\\r)", "; "),
                        segment.StartTime.ToShortTimeString() + " - " + segment.EndTime.ToShortTimeString(),
                        segment.Duration));
            }

            using (var writer = new StreamWriter(new FileStream(sfd.FileName, FileMode.Create, FileAccess.Write, FileShare.None), Encoding.UTF8))
            {
                writer.Write(buffer.ToString());
            }
        }
    }
}
